package client

import (
	"accounts"
	"bytes"
	"core"
	"crypto/rand"
	"crypto/sm2"
	"crypto/sm4"
	"errors"
	"fmt"
	"github.com/ethereum/go-ethereum/rlp"
	"io/ioutil"
	"os"
	"p2p"
	"protocol"
	"reflect"
	"time"
)

type Client struct {
	*accounts.Account
	PrivateKey *sm2.PrivateKey
	protocol.Service
}

//作为程序最重要的部件 必须最先被New
var (
	ClientInfo        *Client
	countPeer         int
	txcommitchan      = make(chan protocol.Message, 1024)
	accountcommitchan = make(chan protocol.Message, 1024)
)

func NewClient() {
	ClientInfo = &Client{
		&accounts.Account{},
		nil,
		protocol.Service{
			Name:        "Client",
			SendPool:    nil,
			ReceivePool: make(chan protocol.Message, 1024),
		},
	}
}

func (c *Client) RegisterAccount(Name string, Email string, Phone string, Password string) error {

	ac := &accounts.Account{}
	ac.Index = 0
	ac.Name = Name
	ac.Email = Email
	ac.Phone = Phone
	ac.Balance = 10000
	ac.Count = 0
	ac.Allowance = 0

	//创建公私钥对
	priv, err := sm2.GenerateKey()
	if err != nil {
		return err
	}
	c.PrivateKey = priv
	if c.PrivateKey == nil {
		return errors.New("GenerateKey Fail")
	}
	ac.PublicKey = core.FromECDSAPub(&priv.PublicKey)

	msg := protocol.NewMessage(protocol.RegisterAccount, ac)
	c.SendPool <- *msg

	if WaitCommit(*ac) {
		c.Account = ac
		err := c.SaveClient(Password)
		if err != nil {
			fmt.Println("SaveClient Err", err)
			return err
		}
		return nil
	} else {
		return errors.New("Fail")
	}
}

func WaitCommit(val interface{}) bool {
	//等待其他节点确认
	countSuccess := 0
	countFail := 0
	stop := make(chan struct{}, 1)
	switch value := val.(type) {
	case core.Transaction:
		fmt.Println("core.Transaction")
		go func() {
			for {
				select {
				case <-stop:
					return
				default:
				}
				msg := <-txcommitchan
				tx := core.Transaction{}
				err := rlp.DecodeBytes(msg.Payload, &tx)
				if err != nil {
					fmt.Println(err)
				}
				if reflect.DeepEqual(value, tx) {
					if msg.Code == protocol.TransactionSuccess {
						countSuccess++
					} else if msg.Code == protocol.TransactionFail {
						countFail++
					}
				} else {
					txcommitchan <- msg
				}
				time.Sleep(100 * time.Microsecond)
			}

		}()
	case accounts.Account:
		fmt.Println("accounts.Account")
		go func() {
			for {
				select {
				case <-stop:
					return
				default:
				}
				msg := <-accountcommitchan
				ac := accounts.Account{}
				err := rlp.DecodeBytes(msg.Payload, &ac)
				if err != nil {
					fmt.Println(err)
				}
				if reflect.DeepEqual(value, ac) {
					if msg.Code == protocol.RegisterAccountSuccess {
						countSuccess++
					} else if msg.Code == protocol.RegisterAccountFail {
						countFail++
					}
				} else {
					accountcommitchan <- msg
				}
				time.Sleep(100 * time.Microsecond)
			}
		}()
	default:
		fmt.Println("unknown", value)
	}
	for {
		//fmt.Println("当前节点数量：",countPeer)
		//fmt.Println("当前通过节点数量：",countSuccess)
		//fmt.Println("当前拒绝节点数量：",countFail)
		time.Sleep(100 * time.Microsecond)
		//如果节点数量小于4 无法构建PBFT网络 少数服从多数
		if countPeer < 4 {
			if countSuccess+countFail == countPeer {
				stop <- struct{}{}
				if countSuccess >= countFail {
					fmt.Println("当前节点数量：", countPeer)
					fmt.Println("当前通过节点数量：", countSuccess)
					fmt.Println("当前拒绝节点数量：", countFail)
					return true
				} else {
					fmt.Println("当前节点数量：", countPeer)
					fmt.Println("当前通过节点数量：", countSuccess)
					fmt.Println("当前拒绝节点数量：", countFail)
					return false
				}
			}
		} else {
			if countSuccess >= (countPeer+2)/3 {
				stop <- struct{}{}
				return true
			}
			if countFail >= (countPeer+2)/3 {
				stop <- struct{}{}
				return false
			}
		}
	}
}

func (c *Client) GetClientFromFile(phone string, password string) error {
	key, err := core.CalculateHash([]byte(password), core.SM3)
	key = key[:16]

	//验证口令是否正确
	data, err := ioutil.ReadFile(phone + "test.config")
	if err != nil {
		return err
	}
	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return err
	}
	d0 := make([]byte, 16)
	cipher.Decrypt(d0, data)
	if bytes.Equal(d0, []byte("testtesttesttest")) == false {
		fmt.Println("口令错误")
		return errors.New("password fail")
	}

	//读取私钥
	c.PrivateKey, err = sm2.ReadPrivateKeyFromPem(phone+"priv.pem", key) // 读取密钥
	if err != nil {
		fmt.Println("读取密钥失败", err)
		return errors.New("ReadPrivateKeyFromPem Fail")
	}

	c.PublicKey = core.FromECDSAPub(&c.PrivateKey.PublicKey)

	//读取Account信息
	for k := range accounts.AcManager.Accounts {
		if bytes.Equal(c.PublicKey, accounts.AcManager.Accounts[k].PublicKey) {
			c.Account = accounts.AcManager.Accounts[k]
			return nil
		}
	}
	fmt.Println("ReadAccount Fail")
	return errors.New("ReadAccount Fail")

}

func (c *Client) SaveClient(password string) error {
	key, err := core.CalculateHash([]byte(password), core.SM3)
	key = key[:16]
	//写入口令正确性测试文件
	data := []byte("testtesttesttest")
	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return err
	}
	d0 := make([]byte, 16)
	cipher.Encrypt(d0, data)
	// 创建文件
	file, err := os.OpenFile(c.Phone+"test.config", os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	// 写入文件
	n, err := file.Write(d0)
	// 当 n != len(b) 时，返回错误
	if err == nil && n != len(d0) {
		return errors.New("Write fail")
	}
	file.Close()

	//写入私钥文件
	ok, err := sm2.WritePrivateKeytoPem(c.Phone+"priv.pem", c.PrivateKey, key) // 生成密钥文件
	if ok != true {
		return err
	}
	return nil
}

func (c *Client) ChangePassword(newPassword string, oldPassword string) error {

	key, err := core.CalculateHash([]byte(oldPassword), core.SM3)
	key = key[:16]

	if err != nil {
		return err
	}
	//验证口令是否正确
	data, err := ioutil.ReadFile(c.Phone + "test.config")
	if err != nil {
		return err
	}
	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return err
	}
	d0 := make([]byte, 16)
	cipher.Decrypt(d0, data)
	if bytes.Equal(d0, []byte("testtesttesttest")) == false {
		return errors.New("password fail")
	}

	return c.SaveClient(newPassword)
}

func (c *Client) ReceiveMsg() {
	go func() {
		for {
			msg := <-c.ReceivePool
			switch msg.Code {
			case protocol.RegisterAccountSuccess:
				accountcommitchan <- msg

			case protocol.RegisterAccountFail:
				accountcommitchan <- msg

			case protocol.TransactionSuccess:
				txcommitchan <- msg

			case protocol.TransactionFail:
				txcommitchan <- msg

			default:
				fmt.Println("Client 未知的类型")
			}
		}
	}()
}

func (c *Client) StartClient() {
	//更新节点数量
	go func() {
		for {
			countPeer = p2p.GetPeerNumber()
			time.Sleep(5 * time.Second)
		}
	}()
	go c.ReceiveMsg()
}

func (c *Client) SendTransaction(dst []byte, amount uint64) error {

	tx := core.Transaction{
		c.PublicKey,
		dst,
		c.Count + 1,
		uint64(time.Now().Unix()),
		amount,
		amount / 1000,
		core.SM3,
		[]byte(""),
		[]byte(""),
	}
	data, err := rlp.EncodeToBytes(tx)
	if err != nil {
		return err
	}
	tx.Hash, err = core.CalculateHash(data, tx.HashMethod)
	if err != nil {
		return err
	}
	tx.Signature, err = c.PrivateKey.Sign(rand.Reader, tx.Hash, nil) // 签名
	if err != nil {
		return err
	}
	msg := protocol.NewMessage(protocol.Transaction, tx)
	msg.Source = p2p.LocalIP
	c.SendPool <- *msg

	fmt.Println("交易构建完成：", tx)

	if WaitCommit(tx) {
		fmt.Println("转账成功")
		return nil
	} else {
		fmt.Println("转账失败")
		return errors.New("Fail")
	}

}
